namespace ts {
    export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "kconfig.json"): string | undefined {
        return forEachAncestorDirectory(searchPath, ancestor => {
            const fileName = combinePaths(ancestor, configName);
            return fileExists(fileName) ? fileName : undefined;
        });
    }

    export function resolveTripleslashReference(moduleName: string, containingFile: string): string {
        const basePath = getDirectoryPath(containingFile);
        const referencedFileName = isRootedDiskPath(moduleName) ? moduleName : combinePaths(basePath, moduleName);
        return normalizePath(referencedFileName);
    }

    /* @internal */
    export function computeCommonSourceDirectoryOfFilenames(fileNames: string[], currentDirectory: string, getCanonicalFileName: GetCanonicalFileName): string {
        let commonPathComponents: string[] | undefined;
        const failed = forEach(fileNames, sourceFile => {
            // Each file contributes into common source file path
            const sourcePathComponents = getNormalizedPathComponents(sourceFile, currentDirectory);
            sourcePathComponents.pop(); // The base file name is not part of the common directory path

            if (!commonPathComponents) {
                // first file
                commonPathComponents = sourcePathComponents;
                return;
            }

            const n = Math.min(commonPathComponents.length, sourcePathComponents.length);
            for (let i = 0; i < n; i++) {
                if (getCanonicalFileName(commonPathComponents[i]) !== getCanonicalFileName(sourcePathComponents[i])) {
                    if (i === 0) {
                        // Failed to find any common path component
                        return true;
                    }

                    // New common path found that is 0 -> i-1
                    commonPathComponents.length = i;
                    break;
                }
            }

            // If the sourcePathComponents was shorter than the commonPathComponents, truncate to the sourcePathComponents
            if (sourcePathComponents.length < commonPathComponents.length) {
                commonPathComponents.length = sourcePathComponents.length;
            }
        });

        // A common path can not be found when paths span multiple drives on windows, for example
        if (failed) {
            return "";
        }

        if (!commonPathComponents) { // Can happen when all input files are .d.ts files
            return currentDirectory;
        }

        return getPathFromPathComponents(commonPathComponents);
    }

    interface OutputFingerprint {
        hash: string;
        byteOrderMark: boolean;
        mtime: Date;
    }

    export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost {
        return createCompilerHostWorker(options, setParentNodes);
    }

    /*@internal*/
    // TODO(shkamat): update this after reworking ts build API
    export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost {
        const existingDirectories = createMap<boolean>();
        const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
        function getSourceFile(fileName: string, onError?: (message: string) => void): SourceFile | undefined {
            let text: string | undefined;
            try {
                performance.mark("beforeIORead");
                text = compilerHost.readFile(fileName);
                performance.mark("afterIORead");
                performance.measure("I/O Read", "beforeIORead", "afterIORead");
            }
            catch (e) {
                if (onError) {
                    onError(e.message);
                }
                text = "";
            }
            return text !== undefined ? createSourceFile(fileName, text, setParentNodes) : undefined;
        }

        function directoryExists(directoryPath: string): boolean {
            if (existingDirectories.has(directoryPath)) {
                return true;
            }
            if (system.directoryExists(directoryPath)) {
                existingDirectories.set(directoryPath, true);
                return true;
            }
            return false;
        }

        function ensureDirectoriesExist(directoryPath: string) {
            if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
                const parentDirectory = getDirectoryPath(directoryPath);
                ensureDirectoriesExist(parentDirectory);
                if (compilerHost.createDirectory) {
                    compilerHost.createDirectory(directoryPath);
                }
                else {
                    system.createDirectory(directoryPath);
                }
            }
        }

        let outputFingerprints: Map<OutputFingerprint>;

        function writeFileIfUpdated(fileName: string, data: string, writeByteOrderMark: boolean): void {
            if (!outputFingerprints) {
                outputFingerprints = createMap<OutputFingerprint>();
            }

            const hash = system.createHash!(data); // TODO: GH#18217
            const mtimeBefore = system.getModifiedTime!(fileName); // TODO: GH#18217

            if (mtimeBefore) {
                const fingerprint = outputFingerprints.get(fileName);
                // If output has not been changed, and the file has no external modification
                if (fingerprint &&
                    fingerprint.byteOrderMark === writeByteOrderMark &&
                    fingerprint.hash === hash &&
                    fingerprint.mtime.getTime() === mtimeBefore.getTime()) {
                    return;
                }
            }

            system.writeFile(fileName, data, writeByteOrderMark);

            const mtimeAfter = system.getModifiedTime!(fileName) || missingFileModifiedTime; // TODO: GH#18217

            outputFingerprints.set(fileName, {
                hash,
                byteOrderMark: writeByteOrderMark,
                mtime: mtimeAfter
            });
        }

        function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
            try {
                performance.mark("beforeIOWrite");
                ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));

                if (isWatchSet(options) && system.createHash && system.getModifiedTime) {
                    writeFileIfUpdated(fileName, data, writeByteOrderMark);
                }
                else {
                    system.writeFile(fileName, data, writeByteOrderMark);
                }

                performance.mark("afterIOWrite");
                performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
            }
            catch (e) {
                if (onError) {
                    onError(e.message);
                }
            }
        }

        const newLine = getNewLineCharacter(options, () => system.newLine);
        const realpath = system.realpath && ((path: string) => system.realpath!(path));
        const 当前目录 = system.getCurrentDirectory();
        const 全局模块目录 = normalizePath(combinePaths(getDirectoryPath(system.getExecutingFilePath()), '模块'));
        const 当前模块目录 = normalizePath(combinePaths(当前目录, '模块'));
        const 当前源码目录 = normalizePath(combinePaths(当前目录, '源码'));
        const 当前项目配置 = normalizePath(combinePaths(当前目录, 'klang.mod.json'));        

        function getDefaultLibLocation(): string {
            return normalizePath(combinePaths(全局模块目录, 'lib'));
        }

        const compilerHost: CompilerHost = {
            getSourceFile,
            getDefaultLibLocation,
            getDefaultLibFileName: () => combinePaths(getDefaultLibLocation() , getDefaultLibFileName()),
            writeFile,
            getCurrentDirectory: memoize(() => system.getCurrentDirectory()),
            useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
            getCanonicalFileName,
            getNewLine: () => newLine,
            fileExists: fileName => system.fileExists(fileName),
            readFile: fileName => system.readFile(fileName),
            trace: (s: string) => system.write(s + newLine),
            directoryExists: directoryName => system.directoryExists(directoryName),
            getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
            getDirectories: (path: string) => system.getDirectories(path),
            realpath,
            readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth),
            createDirectory: d => system.createDirectory(d),
            createHash: maybeBind(system, system.createHash),
            执行: (命令: string, 参数: string[], op: ProcessEnvOptions) => system.执行!(命令, 参数, op),
            全局模块目录,
            当前模块目录,
            当前源码目录,
            当前项目配置,
        };
        return compilerHost;
    }

    /*@internal*/
    interface CompilerHostLikeForCache {
        fileExists(fileName: string): boolean;
        readFile(fileName: string, encoding?: string): string | undefined;
        directoryExists?(directory: string): boolean;
        createDirectory?(directory: string): void;
        writeFile?: WriteFileCallback;
    }

    /*@internal*/
    export function changeCompilerHostLikeToUseCache(
        host: CompilerHostLikeForCache,
        toPath: (fileName: string) => Path,
        getSourceFile?: CompilerHost["getSourceFile"]
    ) {
        const originalReadFile = host.readFile;
        const originalFileExists = host.fileExists;
        const originalDirectoryExists = host.directoryExists;
        const originalCreateDirectory = host.createDirectory;
        const originalWriteFile = host.writeFile;
        const readFileCache = createMap<string | false>();
        const fileExistsCache = createMap<boolean>();
        const directoryExistsCache = createMap<boolean>();
        const sourceFileCache = createMap<SourceFile>();

        const readFileWithCache = (fileName: string): string | undefined => {
            const key = toPath(fileName);
            const value = readFileCache.get(key);
            if (value !== undefined) return value !== false ? value : undefined;
            return setReadFileCache(key, fileName);
        };
        const setReadFileCache = (key: Path, fileName: string) => {
            const newValue = originalReadFile.call(host, fileName);
            readFileCache.set(key, newValue !== undefined ? newValue : false);
            return newValue;
        };
        host.readFile = fileName => {
            const key = toPath(fileName);
            const value = readFileCache.get(key);
            if (value !== undefined) return value !== false ? value : undefined; // could be .d.ts from output
            // Cache json or buildInfo
            if (!fileExtensionIs(fileName, Extension.Json) && !isBuildInfoFile(fileName)) {
                return originalReadFile.call(host, fileName);
            }

            return setReadFileCache(key, fileName);
        };

        const getSourceFileWithCache: CompilerHost["getSourceFile"] | undefined = getSourceFile ? (fileName, onError, shouldCreateNewSourceFile) => {
            const key = toPath(fileName);
            const value = sourceFileCache.get(key);
            if (value) return value;

            const sourceFile = getSourceFile(fileName, onError, shouldCreateNewSourceFile);
            if (sourceFile && (isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json))) {
                sourceFileCache.set(key, sourceFile);
            }
            return sourceFile;
        } : undefined;

        // fileExists for any kind of extension
        host.fileExists = fileName => {
            const key = toPath(fileName);
            const value = fileExistsCache.get(key);
            if (value !== undefined) return value;
            const newValue = originalFileExists.call(host, fileName);
            fileExistsCache.set(key, !!newValue);
            return newValue;
        };
        if (originalWriteFile) {
            host.writeFile = (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
                const key = toPath(fileName);
                fileExistsCache.delete(key);

                const value = readFileCache.get(key);
                if (value !== undefined && value !== data) {
                    readFileCache.delete(key);
                    sourceFileCache.delete(key);
                }
                else if (getSourceFileWithCache) {
                    const sourceFile = sourceFileCache.get(key);
                    if (sourceFile && sourceFile.text !== data) {
                        sourceFileCache.delete(key);
                    }
                }
                originalWriteFile.call(host, fileName, data, writeByteOrderMark, onError, sourceFiles);
            };
        }

        // directoryExists
        if (originalDirectoryExists && originalCreateDirectory) {
            host.directoryExists = directory => {
                const key = toPath(directory);
                const value = directoryExistsCache.get(key);
                if (value !== undefined) return value;
                const newValue = originalDirectoryExists.call(host, directory);
                directoryExistsCache.set(key, !!newValue);
                return newValue;
            };
            host.createDirectory = directory => {
                const key = toPath(directory);
                directoryExistsCache.delete(key);
                originalCreateDirectory.call(host, directory);
            };
        }

        return {
            originalReadFile,
            originalFileExists,
            originalDirectoryExists,
            originalCreateDirectory,
            originalWriteFile,
            getSourceFileWithCache,
            readFileWithCache
        };
    }

    export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
    /*@internal*/ export function getPreEmitDiagnostics(program: BuilderProgram, sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[]; // eslint-disable-line @typescript-eslint/unified-signatures
    export function getPreEmitDiagnostics(program: Program | BuilderProgram, sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
        const diagnostics = [
            ...program.getConfigFileParsingDiagnostics(),
            ...program.getOptionsDiagnostics(cancellationToken),
            ...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
            ...program.getGlobalDiagnostics(cancellationToken),
            ...program.getSemanticDiagnostics(sourceFile, cancellationToken)
        ];

        if (getEmitDeclarations()) {
            addRange(diagnostics, program.getDeclarationDiagnostics(sourceFile, cancellationToken));
        }

        return sortAndDeduplicateDiagnostics(diagnostics);
    }

    export interface FormatDiagnosticsHost {
        getCurrentDirectory(): string;
        getCanonicalFileName(fileName: string): string;
        getNewLine(): string;
    }

    export function formatDiagnostics(diagnostics: readonly Diagnostic[], host: FormatDiagnosticsHost): string {
        let output = "";

        for (const diagnostic of diagnostics) {
            output += formatDiagnostic(diagnostic, host);
        }
        return output;
    }

    export function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string {
        const errorMessage = `${diagnosticCategoryName(diagnostic)} TS${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine())}${host.getNewLine()}`;

        if (diagnostic.file) {
            const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!); // TODO: GH#18217
            const fileName = diagnostic.file.fileName;
            const relativeFileName = convertToRelativePath(fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName));
            return `${relativeFileName}(${line + 1},${character + 1}): ` + errorMessage;
        }

        return errorMessage;
    }

    /** @internal */
    export enum ForegroundColorEscapeSequences {
        Grey = "\u001b[90m",
        Red = "\u001b[91m",
        Yellow = "\u001b[93m",
        Blue = "\u001b[94m",
        Cyan = "\u001b[96m"
    }
    const gutterStyleSequence = "\u001b[7m";
    const gutterSeparator = " ";
    const resetEscapeSequence = "\u001b[0m";
    const ellipsis = "...";
    const halfIndent = "  ";
    const indent = "    ";
    function getCategoryFormat(category: DiagnosticCategory): ForegroundColorEscapeSequences {
        switch (category) {
            case DiagnosticCategory.Error: return ForegroundColorEscapeSequences.Red;
            case DiagnosticCategory.Warning: return ForegroundColorEscapeSequences.Yellow;
            case DiagnosticCategory.Suggestion: return Debug.fail("Should never get an Info diagnostic on the command line.");
            case DiagnosticCategory.Message: return ForegroundColorEscapeSequences.Blue;
        }
    }

    /** @internal */
    export function formatColorAndReset(text: string, formatStyle: string) {
        return formatStyle + text + resetEscapeSequence;
    }

    function padLeft(s: string, length: number) {
        while (s.length < length) {
            s = " " + s;
        }
        return s;
    }

    function formatCodeSpan(file: SourceFile, start: number, length: number, indent: string, squiggleColor: ForegroundColorEscapeSequences, host: FormatDiagnosticsHost) {
        const { line: firstLine, character: firstLineChar } = getLineAndCharacterOfPosition(file, start);
        const { line: lastLine, character: lastLineChar } = getLineAndCharacterOfPosition(file, start + length);
        const lastLineInFile = getLineAndCharacterOfPosition(file, file.text.length).line;

        const hasMoreThanFiveLines = (lastLine - firstLine) >= 4;
        let gutterWidth = (lastLine + 1 + "").length;
        if (hasMoreThanFiveLines) {
            gutterWidth = Math.max(ellipsis.length, gutterWidth);
        }

        let context = "";
        for (let i = firstLine; i <= lastLine; i++) {
            context += host.getNewLine();
            // If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
            // so we'll skip ahead to the second-to-last line.
            if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) {
                context += indent + formatColorAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + host.getNewLine();
                i = lastLine - 1;
            }

            const lineStart = getPositionOfLineAndCharacter(file, i, 0);
            const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter(file, i + 1, 0) : file.text.length;
            let lineContent = file.text.slice(lineStart, lineEnd);
            lineContent = lineContent.replace(/\s+$/g, "");  // trim from end
            lineContent = lineContent.replace("\t", " ");    // convert tabs to single spaces

            // Output the gutter and the actual contents of the line.
            context += indent + formatColorAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator;
            context += lineContent + host.getNewLine();

            // Output the gutter and the error span for the line using tildes.
            context += indent + formatColorAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator;
            context += squiggleColor;
            if (i === firstLine) {
                // If we're on the last line, then limit it to the last character of the last line.
                // Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
                const lastCharForLine = i === lastLine ? lastLineChar : undefined;

                context += lineContent.slice(0, firstLineChar).replace(/\S/g, " ");
                context += lineContent.slice(firstLineChar, lastCharForLine).replace(/./g, "~");
            }
            else if (i === lastLine) {
                context += lineContent.slice(0, lastLineChar).replace(/./g, "~");
            }
            else {
                // Squiggle the entire line.
                context += lineContent.replace(/./g, "~");
            }
            context += resetEscapeSequence;
        }
        return context;
    }

    /* @internal */
    export function formatLocation(file: SourceFile, start: number, host: FormatDiagnosticsHost, color = formatColorAndReset) {
        const { line: firstLine, character: firstLineChar } = getLineAndCharacterOfPosition(file, start); // TODO: GH#18217
        const relativeFileName = host ? convertToRelativePath(file.fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName)) : file.fileName;

        let output = "";
        output += color(relativeFileName, ForegroundColorEscapeSequences.Cyan);
        output += ":";
        output += color(`${firstLine + 1}`, ForegroundColorEscapeSequences.Yellow);
        output += ":";
        output += color(`${firstLineChar + 1}`, ForegroundColorEscapeSequences.Yellow);
        return output;
    }

    export function formatDiagnosticsWithColorAndContext(diagnostics: readonly Diagnostic[], host: FormatDiagnosticsHost): string {
        let output = "";
        for (const diagnostic of diagnostics) {
            if (diagnostic.file) {
                const { file, start } = diagnostic;
                output += formatLocation(file, start!, host); // TODO: GH#18217
                output += " - ";
            }

            output += formatColorAndReset(diagnosticCategoryName(diagnostic), getCategoryFormat(diagnostic.category));
            output += formatColorAndReset(` TS${diagnostic.code}: `, ForegroundColorEscapeSequences.Grey);
            output += flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine());

            if (diagnostic.file) {
                output += host.getNewLine();
                output += formatCodeSpan(diagnostic.file, diagnostic.start!, diagnostic.length!, "", getCategoryFormat(diagnostic.category), host); // TODO: GH#18217
                if (diagnostic.relatedInformation) {
                    output += host.getNewLine();
                    for (const { file, start, length, messageText } of diagnostic.relatedInformation) {
                        if (file) {
                            output += host.getNewLine();
                            output += halfIndent + formatLocation(file, start!, host); // TODO: GH#18217
                            output += formatCodeSpan(file, start!, length!, indent, ForegroundColorEscapeSequences.Cyan, host); // TODO: GH#18217
                        }
                        output += host.getNewLine();
                        output += indent + flattenDiagnosticMessageText(messageText, host.getNewLine());
                    }
                }
            }

            output += host.getNewLine();
        }
        return output;
    }

    export function flattenDiagnosticMessageText(diag: string | DiagnosticMessageChain | undefined, newLine: string, indent = 0): string {
        if (isString(diag)) {
            return diag;
        }
        else if (diag === undefined) {
            return "";
        }
        let result = "";
        if (indent) {
            result += newLine;

            for (let i = 0; i < indent; i++) {
                result += "  ";
            }
        }
        result += diag.messageText;
        indent++;
        if (diag.next) {
            for (const kid of diag.next) {
                result += flattenDiagnosticMessageText(kid, newLine, indent);
            }
        }
        return result;
    }

    /* @internal */
    export function loadWithLocalCache<T>(names: string[], containingFile: string, loader: (name: string, containingFile: string) => T): T[] {
        if (names.length === 0) {
            return [];
        }
        const resolutions: T[] = [];
        const cache = createMap<T>();
        for (const name of names) {
            let result: T;
            if (cache.has(name)) {
                result = cache.get(name)!;
            }
            else {
                cache.set(name, result = loader(name, containingFile));
            }
            resolutions.push(result);
        }
        return resolutions;
    }

    /* @internal */
    export const inferredTypesContainingFile = "__inferred type names__.ts";

    interface DiagnosticCache<T extends Diagnostic> {
        perFile?: Map<T[]>;
        allDiagnostics?: Diagnostic[];
    }

    interface RefFile extends TextRange {
        kind: RefFileKind;
        index: number;
        file: SourceFile;
    }

    /**
     * Determines if program structure is upto date or needs to be recreated
     */
    /* @internal */
    export function isProgramUptoDate(
        program: Program | undefined,
        rootFileNames: string[],
        newOptions: CompilerOptions,
        getSourceVersion: (path: Path) => string | undefined,
        fileExists: (fileName: string) => boolean,
        hasInvalidatedResolution: HasInvalidatedResolution,
        hasChangedAutomaticTypeDirectiveNames: boolean
    ): boolean {
        // If we haven't created a program yet or have changed automatic type directives, then it is not up-to-date
        if (!program || hasChangedAutomaticTypeDirectiveNames) {
            return false;
        }

        // If number of files in the program do not match, it is not up-to-date
        if (program.getRootFileNames().length !== rootFileNames.length) {
            return false;
        }

        // If any file is not up-to-date, then the whole program is not up-to-date
        if (program.getSourceFiles().some(sourceFileNotUptoDate)) {
            return false;
        }

        // If any of the missing file paths are now created
        if (program.getMissingFilePaths().some(fileExists)) {
            return false;
        }

        const currentOptions = program.getCompilerOptions();
        // If the compilation settings do no match, then the program is not up-to-date
        if (!compareDataObjects(currentOptions, newOptions)) {
            return false;
        }

        return true;

        function sourceFileNotUptoDate(sourceFile: SourceFile) {
            return !sourceFileVersionUptoDate(sourceFile) ||
                hasInvalidatedResolution(sourceFile.path);
        }

        function sourceFileVersionUptoDate(sourceFile: SourceFile) {
            return sourceFile.version === getSourceVersion(sourceFile.resolvedPath);
        }
    }

    export function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): readonly Diagnostic[] {
        return configFileParseResult.errors;
    }

    /**
     * Determine if source file needs to be re-created even if its text hasn't changed
     */
    function shouldProgramCreateNewSourceFiles(program: Program | undefined, newOptions: CompilerOptions): boolean {
        if (!program) return false;
        // If any compiler options change, we can't reuse old source file even if version match
        // The change in options like these could result in change in syntax tree or `sourceFile.bindDiagnostics`.
        const oldOptions = program.getCompilerOptions();
        return !!isJsonEqual(oldOptions, newOptions);
    }

    function createCreateProgramOptions(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: readonly Diagnostic[]): CreateProgramOptions {
        return {
            rootNames,
            options,
            host,
            oldProgram,
            configFileParsingDiagnostics
        };
    }

    /**
     * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions'
     * that represent a compilation unit.
     *
     * Creating a program proceeds from a set of root files, expanding the set of inputs by following imports and
     * triple-slash-reference-path directives transitively. '@types' and triple-slash-reference-types are also pulled in.
     *
     * @param createProgramOptions - The options for creating a program.
     * @returns A 'Program' object.
     */
    export function createProgram(createProgramOptions: CreateProgramOptions): Program;
    /**
     * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions'
     * that represent a compilation unit.
     *
     * Creating a program proceeds from a set of root files, expanding the set of inputs by following imports and
     * triple-slash-reference-path directives transitively. '@types' and triple-slash-reference-types are also pulled in.
     *
     * @param rootNames - A set of root files.
     * @param options - The compiler options which should be used.
     * @param host - The host interacts with the underlying file system.
     * @param oldProgram - Reuses an old program structure.
     * @param configFileParsingDiagnostics - error during config file parsing
     * @returns A 'Program' object.
     */
    export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: readonly Diagnostic[]): Program;
    export function createProgram(rootNamesOrOptions: string[] | CreateProgramOptions, _options?: CompilerOptions, _host?: CompilerHost, _oldProgram?: Program, _configFileParsingDiagnostics?: readonly Diagnostic[]): Program {
        const createProgramOptions = isArray(rootNamesOrOptions) ? createCreateProgramOptions(rootNamesOrOptions, _options!, _host, _oldProgram, _configFileParsingDiagnostics) : rootNamesOrOptions; // TODO: GH#18217
        const { options, configFileParsingDiagnostics } = createProgramOptions;
        let { rootNames , oldProgram } = createProgramOptions;

        let processingDefaultLibFiles: SourceFile[] | undefined;
        let processingOtherFiles: SourceFile[] | undefined;
        let files: SourceFile[];
        let symlinks: ReadonlyMap<string> | undefined;
        let commonSourceDirectory: string;
        let diagnosticsProducingTypeChecker: TypeChecker;
        let noDiagnosticsTypeChecker: TypeChecker;
        let classifiableNames: UnderscoreEscapedMap<true>;
        const ambientModuleNameToUnmodifiedFileName = createMap<string>();
        // Todo:: Use this to report why file was included in --extendedDiagnostics
        let refFileMap: MultiMap<ts.RefFile> | undefined;

        const cachedSemanticDiagnosticsForFile: DiagnosticCache<Diagnostic> = {};
        const cachedDeclarationDiagnosticsForFile: DiagnosticCache<DiagnosticWithLocation> = {};

        let fileProcessingDiagnostics = createDiagnosticCollection();

        // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
        // This works as imported modules are discovered recursively in a depth first manner, specifically:
        // - For each root file, findSourceFile is called.
        // - This calls processImportedModules for each module imported in the source file.
        // - This calls resolveModuleNames, and then calls findSourceFile for each resolved module.
        // As all these operations happen - and are nested - within the createProgram call, they close over the below variables.
        // The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses.
        const maxNodeModuleJsDepth = 0;
        let currentNodeModulesDepth = 0;

        // If a module has some of its imports skipped due to being at the depth limit under node_modules, then track
        // this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed.
        const modulesWithElidedImports = createMap<boolean>();

        // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled.
        const sourceFilesFoundSearchingNodeModules = createMap<boolean>();

        performance.mark("beforeProgram");

        const host = createProgramOptions.host || createCompilerHost(options);

        if((!rootNames || !rootNames.length) && host.当前源码目录 ){
            rootNames = host.readDirectory!(host.当前源码目录, [Extension.Dk, Extension.K], [Extension.Json, Extension.TestK, Extension.KBuildInfo], [Extension.K, Extension.Dk]);
        }

        const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName());
        const programDiagnostics = createDiagnosticCollection();
        const currentDirectory = host.getCurrentDirectory();
        const supportedExtensions = getSupportedExtensions();
        const supportedExtensionsWithJsonIfResolveJsonModule = getSuppoertedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions);      
        // Map storing if there is emit blocking diagnostics for given input
        const hasEmitBlockingDiagnostics = createMap<boolean>();

        let moduleResolutionCache: ModuleResolutionCache | undefined;
        let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[]) => 解析的模块名称_[];
        const hasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse;
        if (host.resolveModuleNames) {
            resolveModuleNamesWorker = (moduleNames, containingFile, reusedNames) => host.resolveModuleNames!(Debug.assertEachDefined(moduleNames), containingFile, reusedNames, options).map(resolved => resolved!);
        }
        else {
            moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x));
            const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, host, moduleResolutionCache).模块目录路径!; // TODO: GH#18217
            resolveModuleNamesWorker = (moduleNames, containingFile, _reusedNames) => loadWithLocalCache<解析的模块名称_>(Debug.assertEachDefined(moduleNames), containingFile, loader);
        }

        // Maps from a SourceFile's `.path` to the name of the package it was imported with.
        let sourceFileToPackageName = createMap<string>();
        // Key is a file name. Value is the (non-empty, or undefined) list of files that redirect to it.
        let redirectTargetsMap = createMultiMap<string>();

        /**
         * map with
         * - SourceFile if present
         * - false if sourceFile missing for source of project reference redirect
         * - undefined otherwise
         */
        const filesByName = createMap<SourceFile | false | undefined>();
        let missingFilePaths: readonly Path[] | undefined;
        // stores 'filename -> file association' ignoring case
        // used to track cases when two file names differ only in casing
        const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createMap<SourceFile>() : undefined;

        const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
        // We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
        // `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`.
        let structuralIsReused: StructureIsReused | undefined;
        structuralIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const
        if (structuralIsReused !== StructureIsReused.Completely) {
            processingDefaultLibFiles = [];
            processingOtherFiles = [];
            forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));

            // Do not process the default library if:
            //  - The '--noLib' flag is used.
            //  - A 'no-default-lib' reference comment is encountered in
            //      processing the root files.
            if (rootNames.length) {
                // If '--lib' is not specified, include default library file according to '--target'
                // otherwise, using options specified in '--lib' instead of '--target' default library file
                const defaultLibraryFileName = getDefaultLibraryFileName();
                if (defaultLibraryFileName) {
                    processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true);
                }
            }

            missingFilePaths = arrayFrom(mapDefinedIterator(filesByName.entries(), ([path, file]) => file === undefined ? path as Path : undefined));
            files = processingDefaultLibFiles.concat(processingOtherFiles);
            processingDefaultLibFiles = undefined;
            processingOtherFiles = undefined;
        }

        Debug.assert(!!missingFilePaths);

        // Release any files we have acquired in the old program but are
        // not part of the new program.
        if (oldProgram && host.onReleaseOldSourceFile) {
            const oldSourceFiles = oldProgram.getSourceFiles();
            for (const oldSourceFile of oldSourceFiles) {
                const newFile = getSourceFileByPath(oldSourceFile.resolvedPath);
                if (shouldCreateNewSourceFile || !newFile ||
                    // old file wasnt redirect but new file is
                    (oldSourceFile.resolvedPath === oldSourceFile.path && newFile.resolvedPath !== oldSourceFile.path)) {
                    host.onReleaseOldSourceFile(oldSourceFile, oldProgram.getCompilerOptions(), !!getSourceFileByPath(oldSourceFile.path));
                }
            }
        }

        options.项目名 = (<KlangModConfig>readJson(host.当前项目配置!, host as { readFile(fileName: string): string | undefined })).名称;

        // unconditionally set oldProgram to undefined to prevent it from being captured in closure
        oldProgram = undefined;

        const program: Program = {
            getRootFileNames: () => rootNames,
            getSourceFile,
            getSourceFileByPath,
            getSourceFiles: () => files,
            getMissingFilePaths: () => missingFilePaths!, // TODO: GH#18217
            getRefFileMap: () => refFileMap,
            getCompilerOptions: () => options,
            getSyntacticDiagnostics,
            getOptionsDiagnostics,
            getGlobalDiagnostics,
            getSemanticDiagnostics,
            getSuggestionDiagnostics,
            getDeclarationDiagnostics,
            getTypeChecker,
            getClassifiableNames,
            getDiagnosticsProducingTypeChecker,
            getCommonSourceDirectory,
            emit,
            getCurrentDirectory: () => currentDirectory,
            getNodeCount: () => getDiagnosticsProducingTypeChecker().getNodeCount(),
            getIdentifierCount: () => getDiagnosticsProducingTypeChecker().getIdentifierCount(),
            getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
            getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
            getRelationCacheSizes: () => getDiagnosticsProducingTypeChecker().getRelationCacheSizes(),
            getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
            isSourceFileFromExternalLibrary,
            isSourceFileDefaultLibrary,
            dropDiagnosticsProducingTypeChecker,
            sourceFileToPackageName,
            redirectTargetsMap,
            isEmittedFile,
            getConfigFileParsingDiagnostics,
            getResolvedModuleWithFailedLookupLocationsFromCache,
            emitBuildInfo,
            getProbableSymlinks,
            全局模块目录: host.全局模块目录!,
            当前模块目录: host.当前模块目录!,
            当前源码目录: host.当前源码目录!,
            当前项目配置: host.当前项目配置!,
        };

        performance.mark("afterProgram");
        performance.measure("Program", "beforeProgram", "afterProgram");

        return program;

        function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined {
            return moduleResolutionCache && resolveModuleNameFromCache(moduleName, containingFile, moduleResolutionCache);
        }

        function toPath(fileName: string): Path {
            return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
        }

        function isValidSourceFileForEmit(file: SourceFile) {
            // source file is allowed to be emitted and its not source of project reference redirect
            return sourceFileMayBeEmitted(file, isSourceFileFromExternalLibrary);
        }

        function getCommonSourceDirectory() {
            if (commonSourceDirectory === undefined) {
                const emittedFiles = filter(files, file => isValidSourceFileForEmit(file));
                if (options.configFilePath) {
                    // Project compilations never infer their root from the input source paths
                    commonSourceDirectory = getDirectoryPath(normalizeSlashes(options.configFilePath));
                    checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory);
                }
                else {
                    commonSourceDirectory = computeCommonSourceDirectory(emittedFiles);
                }

                if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) {
                    // Make sure directory path ends with directory separator so this string can directly
                    // used to replace with "" to get the relative path of the source file and the relative path doesn't
                    // start with / making it rooted path
                    commonSourceDirectory += directorySeparator;
                }
            }
            return commonSourceDirectory;
        }

        function getClassifiableNames() {
            if (!classifiableNames) {
                // Initialize a checker so that all our files are bound.
                getTypeChecker();
                classifiableNames = createUnderscoreEscapedMap<true>();

                for (const sourceFile of files) {
                    copyEntries(sourceFile.classifiableNames!, classifiableNames);
                }
            }

            return classifiableNames;
        }

        function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile) {
            if (structuralIsReused === StructureIsReused.Not) {
                // If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
                // the best we can do is fallback to the default logic.
                return resolveModuleNamesWorker(moduleNames, containingFile);
            }

            const oldSourceFile = oldProgram && oldProgram.getSourceFile(containingFile);
            if (oldSourceFile !== file && file.resolvedModules) {
                // `file` was created for the new program.
                //
                // We only set `file.resolvedModules` via work from the current function,
                // so it is defined iff we already called the current function on `file`.
                // That call happened no later than the creation of the `file` object,
                // which per above occurred during the current program creation.
                // Since we assume the filesystem does not change during program creation,
                // it is safe to reuse resolutions from the earlier call.
                const result: 解析的模块名称_[] = [];
                for (const moduleName of moduleNames) {
                    const resolvedModule = file.resolvedModules.get(moduleName)!;
                    result.push(resolvedModule);
                }
                return result;
            }
            // At this point, we know at least one of the following hold:
            // - file has local declarations for ambient modules
            // - old program state is available
            // With this information, we can infer some module resolutions without performing resolution.

            /** An ordered list of module names for which we cannot recover the resolution. */
            let unknownModuleNames: string[] | undefined;
            /**
             * The indexing of elements in this list matches that of `moduleNames`.
             *
             * Before combining results, result[i] is in one of the following states:
             * * undefined: needs to be recomputed,
             * * predictedToResolveToAmbientModuleMarker: known to be an ambient module.
             * Needs to be reset to undefined before returning,
             * * ResolvedModuleFull instance: can be reused.
             */
            let result: 解析的模块名称_[] | undefined;
            let reusedNames: string[] | undefined;
            /** A transient placeholder used to mark predicted resolution in the result list. */
            const predictedToResolveToAmbientModuleMarker: 解析的模块名称_ = <any>{};

            for (let i = 0; i < moduleNames.length; i++) {
                const moduleName = moduleNames[i];
                // If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions
                if (file === oldSourceFile && !hasInvalidatedResolution(oldSourceFile.path)) {
                    const oldResolvedModule = oldSourceFile && oldSourceFile.resolvedModules!.get(moduleName);
                    if (oldResolvedModule) {
                        if (isTraceEnabled(host)) {
                            trace(host, Diagnostics.Reusing_resolution_of_module_0_to_file_1_from_old_program, moduleName, containingFile);
                        }
                        (result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule;
                        (reusedNames || (reusedNames = [])).push(moduleName);
                        continue;
                    }
                }
                // We know moduleName resolves to an ambient module provided that moduleName:
                // - is in the list of ambient modules locally declared in the current source file.
                // - resolved to an ambient module in the old program whose declaration is in an unmodified file
                //   (so the same module declaration will land in the new program)
                let resolvesToAmbientModuleInNonModifiedFile = false;
                resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName);

                if (resolvesToAmbientModuleInNonModifiedFile) {
                    (result || (result = new Array(moduleNames.length)))[i] = predictedToResolveToAmbientModuleMarker;
                }
                else {
                    // Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
                    (unknownModuleNames || (unknownModuleNames = [])).push(moduleName);
                }
            }

            const resolutions = unknownModuleNames && unknownModuleNames.length
                ? resolveModuleNamesWorker(unknownModuleNames, containingFile, reusedNames)
                : emptyArray;

            // Combine results of resolutions and predicted results
            if (!result) {
                // There were no unresolved/ambient resolutions.
                Debug.assert(resolutions.length === moduleNames.length);
                return resolutions;
            }

            let j = 0;
            for (let i = 0; i < result.length; i++) {
                if (result[i]) {
                    // `result[i]` is either a `ResolvedModuleFull` or a marker.
                    // If it is the former, we can leave it as is.
                    if (result[i] === predictedToResolveToAmbientModuleMarker) {
                        result[i] = undefined!; // TODO: GH#18217
                    }
                }
                else {
                    result[i] = resolutions[j];
                    j++;
                }
            }
            Debug.assert(j === resolutions.length);

            return result;

            // If we change our policy of rechecking failed lookups on each program create,
            // we should adjust the value returned here.
            function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string): boolean {
                const resolutionToFile = getResolvedModule(oldSourceFile, moduleName);
                const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.解析的模块目录路径);
                if (resolutionToFile && resolvedFile) {
                    // In the old program, we resolved to an ambient module that was in the same
                    //   place as we expected to find an actual module file.
                    // We actually need to return 'false' here even though this seems like a 'true' case
                    //   because the normal module resolution algorithm will find this anyway.
                    return false;
                }

                // at least one of declarations should come from non-modified source file
                const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName);

                if (!unmodifiedFile) {
                    return false;
                }

                if (isTraceEnabled(host)) {
                    trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, unmodifiedFile);
                }
                return true;
            }
        }

        function tryReuseStructureFromOldProgram(): StructureIsReused {
            if (!oldProgram) {
                return StructureIsReused.Not;
            }

            Debug.assert(!(oldProgram.structureIsReused! & (StructureIsReused.Completely | StructureIsReused.SafeModules)));

            // there is an old program, check if we can reuse its structure
            const oldRootNames = oldProgram.getRootFileNames();
            if (!arrayIsEqualTo(oldRootNames, rootNames)) {
                return oldProgram.structureIsReused = StructureIsReused.Not;
            }

            // check if program source files has changed in the way that can affect structure of the program
            const newSourceFiles: SourceFile[] = [];
            const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
            oldProgram.structureIsReused = StructureIsReused.Completely;

            // If the missing file paths are now present, it can change the progam structure,
            // and hence cant reuse the structure.
            // This is same as how we dont reuse the structure if one of the file from old program is now missing
            if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) {
                return oldProgram.structureIsReused = StructureIsReused.Not;
            }

            const oldSourceFiles = oldProgram.getSourceFiles();
            const enum SeenPackageName { Exists, Modified }
            const seenPackageNames = createMap<SeenPackageName>();

            for (const oldSourceFile of oldSourceFiles) {
                let newSourceFile = host.getSourceFileByPath
                    ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.resolvedPath, /*onError*/ undefined, shouldCreateNewSourceFile)
                    : host.getSourceFile(oldSourceFile.fileName, /*onError*/ undefined, shouldCreateNewSourceFile); // TODO: GH#18217

                if (!newSourceFile) {
                    return oldProgram.structureIsReused = StructureIsReused.Not;
                }

                Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`");

                let fileChanged: boolean;
                if (oldSourceFile.redirectInfo) {
                    // We got `newSourceFile` by path, so it is actually for the unredirected file.
                    // This lets us know if the unredirected file has changed. If it has we should break the redirect.
                    if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) {
                        // Underlying file has changed. Might not redirect anymore. Must rebuild program.
                        return oldProgram.structureIsReused = StructureIsReused.Not;
                    }
                    fileChanged = false;
                    newSourceFile = oldSourceFile; // Use the redirect.
                }
                else if (oldProgram.redirectTargetsMap.has(oldSourceFile.path)) {
                    // If a redirected-to source file changes, the redirect may be broken.
                    if (newSourceFile !== oldSourceFile) {
                        return oldProgram.structureIsReused = StructureIsReused.Not;
                    }
                    fileChanged = false;
                }
                else {
                    fileChanged = newSourceFile !== oldSourceFile;
                }

                // Since the project references havent changed, its right to set originalFileName and resolvedPath here
                newSourceFile.path = oldSourceFile.path;
                newSourceFile.originalFileName = oldSourceFile.originalFileName;
                newSourceFile.resolvedPath = oldSourceFile.resolvedPath;
                newSourceFile.fileName = oldSourceFile.fileName;

                const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path);
                if (packageName !== undefined) {
                    // If there are 2 different source files for the same package name and at least one of them changes,
                    // they might become redirects. So we must rebuild the program.
                    const prevKind = seenPackageNames.get(packageName);
                    const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists;
                    if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) {
                        return oldProgram.structureIsReused = StructureIsReused.Not;
                    }
                    seenPackageNames.set(packageName, newKind);
                }

                if (fileChanged) {
                    // check imports and module augmentations
                    collectExternalModuleReferences(newSourceFile);
                    if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
                        // imports has changed
                        oldProgram.structureIsReused = StructureIsReused.SafeModules;
                    }
                    // tentatively approve the file
                    modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
                }
                else if (hasInvalidatedResolution(oldSourceFile.path)) {
                    // 'module/types' references could have changed
                    oldProgram.structureIsReused = StructureIsReused.SafeModules;

                    // add file to the modified list so that we will resolve it later
                    modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
                }

                // if file has passed all checks it should be safe to reuse it
                newSourceFiles.push(newSourceFile);
            }

            if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
                return oldProgram.structureIsReused;
            }
            // try to verify results of module resolution
            for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
                const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.originalFileName, currentDirectory);
                const moduleNames = getModuleNames(newSourceFile);
                const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile);
                // ensure that module resolution results are still correct
                const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
                if (resolutionsChanged) {
                    oldProgram.structureIsReused = StructureIsReused.SafeModules;
                    newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
                }
                else {
                    newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
                }
            }

            if (oldProgram.structureIsReused !== StructureIsReused.Completely) {
                return oldProgram.structureIsReused;
            }

            if (host.hasChangedAutomaticTypeDirectiveNames) {
                return oldProgram.structureIsReused = StructureIsReused.SafeModules;
            }

            missingFilePaths = oldProgram.getMissingFilePaths();
            refFileMap = oldProgram.getRefFileMap();

            // update fileName -> file mapping
            for (const newSourceFile of newSourceFiles) {
                const filePath = newSourceFile.path;
                addFileToFilesByName(newSourceFile, filePath, newSourceFile.resolvedPath);
                // Set the file as found during node modules search if it was found that way in old progra,
                if (oldProgram.isSourceFileFromExternalLibrary(oldProgram.getSourceFileByPath(newSourceFile.resolvedPath)!)) {
                    sourceFilesFoundSearchingNodeModules.set(filePath, true);
                }
            }

            files = newSourceFiles;
            fileProcessingDiagnostics = oldProgram.getFileProcessingDiagnostics();

            for (const modifiedFile of modifiedSourceFiles) {
                fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile);
            }

            sourceFileToPackageName = oldProgram.sourceFileToPackageName;
            redirectTargetsMap = oldProgram.redirectTargetsMap;

            return oldProgram.structureIsReused = StructureIsReused.Completely;
        }

        function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
            return {
                getPrependNodes,
                getCanonicalFileName,
                getCommonSourceDirectory: program.getCommonSourceDirectory,
                getCompilerOptions: program.getCompilerOptions,
                getCurrentDirectory: () => currentDirectory,
                getNewLine: () => host.getNewLine(),
                getSourceFile: program.getSourceFile,
                getSourceFileByPath: program.getSourceFileByPath,
                getSourceFiles: program.getSourceFiles,
                isSourceFileFromExternalLibrary,
                getProbableSymlinks,
                writeFile: writeFileCallback || (
                    (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
                isEmitBlocked,
                readFile: f => host.readFile(f),
                fileExists: f => {
                    // Use local caches
                    const path = toPath(f);
                    if (getSourceFileByPath(path)) return true;
                    if (contains(missingFilePaths, path)) return false;
                    // Before falling back to the host
                    return host.fileExists(f);
                },
                ...(host.directoryExists ? { directoryExists: f => host.directoryExists!(f) } : {}),
                useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
                getProgramBuildInfo: () => program.getProgramBuildInfo && program.getProgramBuildInfo(),
                redirectTargetsMap,
                全局模块目录: host.全局模块目录!,
                当前模块目录: host.当前模块目录!,
                当前源码目录: host.当前源码目录!,
                当前项目配置: host.当前项目配置!,
            };
        }

        function emitBuildInfo(writeFileCallback?: WriteFileCallback): EmitResult {
            performance.mark("beforeEmit");
            const emitResult = emitFiles(
                notImplementedResolver,
                getEmitHost(writeFileCallback),
                /*targetSourceFile*/ undefined,
                /*transformers*/ noTransformers,
                /*emitOnlyDtsFiles*/ false,
                /*onlyBuildInfo*/ true
            );

            performance.mark("afterEmit");
            performance.measure("Emit", "beforeEmit", "afterEmit");
            return emitResult;
        }

        function getPrependNodes() {
            return emptyArray
        }

        function isSourceFileFromExternalLibrary(file: SourceFile): boolean {
            return !!sourceFilesFoundSearchingNodeModules.get(file.path);
        }

        function isSourceFileDefaultLibrary(file: SourceFile): boolean {          
            const equalityComparer = host.useCaseSensitiveFileNames() ? equateStringsCaseSensitive : equateStringsCaseInsensitive;
            return equalityComparer(file.fileName, getDefaultLibraryFileName());
        }

        function getDiagnosticsProducingTypeChecker() {
            return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
        }

        function dropDiagnosticsProducingTypeChecker() {
            diagnosticsProducingTypeChecker = undefined!;
        }

        function getTypeChecker() {
            return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
        }

        function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
            return runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, transformers, forceDtsEmit));
        }

        function isEmitBlocked(emitFileName: string): boolean {
            return hasEmitBlockingDiagnostics.has(toPath(emitFileName));
        }

        function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
            let declarationDiagnostics: readonly Diagnostic[] = [];

            if (!forceDtsEmit) {
                const diagnostics = [
                    ...program.getOptionsDiagnostics(cancellationToken),
                    ...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
                    ...program.getGlobalDiagnostics(cancellationToken),
                    ...program.getSemanticDiagnostics(sourceFile, cancellationToken)
                ];

                if (diagnostics.length === 0 && getEmitDeclarations()) {
                    declarationDiagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken);
                }

                if (diagnostics.length > 0 || declarationDiagnostics.length > 0) {
                    return {
                        diagnostics: concatenate(diagnostics, declarationDiagnostics),
                        sourceMaps: undefined,
                        emittedFiles: undefined,
                        emitSkipped: true
                    };
                }
            }

            // Create the emit resolver outside of the "emitTime" tracking code below.  That way
            // any cost associated with it (like type checking) are appropriate associated with
            // the type-checking counter.
            //
            // If the -out option is specified, we should not pass the source file to getEmitResolver.
            // This is because in the -out scenario all files need to be emitted, and therefore all
            // files need to be type checked. And the way to specify that all files need to be type
            // checked is to not pass the file to getEmitResolver.
            const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken);

            performance.mark("beforeEmit");

            const emitResult = emitFiles(
                emitResolver,
                getEmitHost(writeFileCallback),
                sourceFile,
                getTransformers(customTransformers),
                /*onlyBuildInfo*/ false,
                forceDtsEmit
            );
            输出生成(program, emitResult, host);
            performance.mark("afterEmit");
            performance.measure("Emit", "beforeEmit", "afterEmit");
            return emitResult;
        }

        function getSourceFile(fileName: string): SourceFile | undefined {
            return getSourceFileByPath(toPath(fileName));
        }

        function getSourceFileByPath(path: Path): SourceFile | undefined {
            return filesByName.get(path) || undefined;
        }

        function getDiagnosticsHelper<T extends Diagnostic>(
            sourceFile: SourceFile,
            getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken) => readonly T[],
            cancellationToken: CancellationToken): readonly T[] {
            if (sourceFile) {
                return getDiagnostics(sourceFile, cancellationToken);
            }
            return sortAndDeduplicateDiagnostics(flatMap(program.getSourceFiles(), sourceFile => {
                if (cancellationToken) {
                    cancellationToken.throwIfCancellationRequested();
                }
                return getDiagnostics(sourceFile, cancellationToken);
            }));
        }

        function getSyntacticDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
            return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken);
        }

        function getSemanticDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly Diagnostic[] {
            return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken);
        }

        function getDeclarationDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
            // collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit)
            if (!sourceFile) {
                return getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
            }
            else {
                return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken);
            }
        }

        function getSyntacticDiagnosticsForFile(sourceFile: SourceFile): readonly DiagnosticWithLocation[] {
            return sourceFile.parseDiagnostics;
        }

        function runWithCancellationToken<T>(func: () => T): T {
            try {
                return func();
            }
            catch (e) {
                if (e instanceof OperationCanceledException) {
                    // We were canceled while performing the operation.  Because our type checker
                    // might be a bad state, we need to throw it away.
                    //
                    // Note: we are overly aggressive here.  We do not actually *have* to throw away
                    // the "noDiagnosticsTypeChecker".  However, for simplicity, i'd like to keep
                    // the lifetimes of these two TypeCheckers the same.  Also, we generally only
                    // cancel when the user has made a change anyways.  And, in that case, we (the
                    // program instance) will get thrown away anyways.  So trying to keep one of
                    // these type checkers alive doesn't serve much purpose.
                    noDiagnosticsTypeChecker = undefined!;
                    diagnosticsProducingTypeChecker = undefined!;
                }

                throw e;
            }
        }

        function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly Diagnostic[] {
            return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedSemanticDiagnosticsForFile, getSemanticDiagnosticsForFileNoCache);
        }

        function getSemanticDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] | undefined {
            return runWithCancellationToken(() => {

                const typeChecker = getDiagnosticsProducingTypeChecker();

                Debug.assert(!!sourceFile.bindDiagnostics);


                const bindDiagnostics: readonly Diagnostic[] =  sourceFile.bindDiagnostics || emptyArray;
                const checkDiagnostics = typeChecker.getDiagnostics(sourceFile, cancellationToken) || emptyArray;
                const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics.getDiagnostics(sourceFile.fileName);
                const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName);

                let diagnostics: Diagnostic[] | undefined;
                for (const diags of [bindDiagnostics, checkDiagnostics, fileProcessingDiagnosticsInFile, programDiagnosticsInFile, undefined]) {
                    if (diags) {
                        for (const diag of diags) {
                            diagnostics = append(diagnostics, diag);
                        }
                    }
                }
                return diagnostics;
            });
        }

        function getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
            return runWithCancellationToken(() => {
                return getDiagnosticsProducingTypeChecker().getSuggestionDiagnostics(sourceFile, cancellationToken);
            });
        }

        function getDeclarationDiagnosticsWorker(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
            return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache);
        }

        function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken) {
            return runWithCancellationToken(() => {
                const resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken);
                // Don't actually write any files since we're just getting diagnostics.
                return ts.getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile);
            });
        }

        function getAndCacheDiagnostics<T extends Diagnostic>(
            sourceFile: SourceFile | undefined,
            cancellationToken: CancellationToken,
            cache: DiagnosticCache<T>,
            getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken) => T[] | undefined,
        ): readonly T[] {

            const cachedResult = sourceFile
                ? cache.perFile && cache.perFile.get(sourceFile.path)
                : cache.allDiagnostics as T[];

            if (cachedResult) {
                return cachedResult;
            }
            const result = getDiagnostics(sourceFile!, cancellationToken) || emptyArray; // TODO: GH#18217
            if (sourceFile) {
                if (!cache.perFile) {
                    cache.perFile = createMap<T[]>();
                }
                cache.perFile.set(sourceFile.path, result);
            }
            else {
                cache.allDiagnostics = result;
            }
            return result;
        }

        function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
            return sourceFile.isDeclarationFile ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
        }

        function getOptionsDiagnostics(): SortedReadonlyArray<Diagnostic> {
            return sortAndDeduplicateDiagnostics(concatenate(
                fileProcessingDiagnostics.getGlobalDiagnostics(),
                concatenate(
                    programDiagnostics.getGlobalDiagnostics(),
                    getOptionsDiagnosticsOfConfigFile()
                )
            ));
        }

        function getOptionsDiagnosticsOfConfigFile() {
            if (!options.configFile) { return emptyArray; }
        }

        function getGlobalDiagnostics(): SortedReadonlyArray<Diagnostic> {
            return rootNames.length ? sortAndDeduplicateDiagnostics(getDiagnosticsProducingTypeChecker().getGlobalDiagnostics().slice()) : emptyArray as any as SortedReadonlyArray<Diagnostic>;
        }

        function getConfigFileParsingDiagnostics(): readonly Diagnostic[] {
            return configFileParsingDiagnostics || emptyArray;
        }

        function processRootFile(fileName: string, isDefaultLib: boolean) {
            processSourceFile(normalizePath(fileName), isDefaultLib, /*packageId*/ undefined);
        }

        function moduleNameIsEqualTo(a: StringLiteralLike | Identifier, b: StringLiteralLike | Identifier): boolean {
            return a.kind === SyntaxKind.Identifier
                ? b.kind === SyntaxKind.Identifier && a.escapedText === b.escapedText
                : b.kind === SyntaxKind.StringLiteral && a.text === b.text;
        }

        function collectExternalModuleReferences(file: SourceFile): void {
            if (file.imports) {
                return;
            }
            let imports: StringLiteral[] | undefined;
            for (const node of file.statements) {
                收集(node);
            }

            function 收集(node: Node){                
                switch (node.kind) {
                    case SyntaxKind.导入声明_:
                        if (!(<导入声明_>node).导入组) break;
                        for (let 目录 of (<导入声明_>node).导入组) {
                            imports = 收集导入成员模块(目录, imports);
                        }
                        break;
                    case SyntaxKind.导入从声明_:
                        imports = append(imports, (<导入从声明_>node).模块名);
                        break;
                    case SyntaxKind.版本语句_:{
                        forEach((<版本语句_>node).thenStatement.statements, 收集);
                        if((<版本语句_>node).elseStatement) {
                            if(是版本语句((<版本语句_>node).elseStatement!)){
                                收集((<版本语句_>node).elseStatement!)
                            }
                            else {
                                forEach(((<版本语句_>node).elseStatement! as Block).statements, 收集);
                            }
                        }
                    }
                }
            }

            file.imports = imports || emptyArray;
            return;
        }

        function 收集导入成员模块(目录: 导入成员_ | 导入版本成员_, imports?: StringLiteral[] | undefined): StringLiteral[] | undefined {
            if (是导入成员(目录)) {
                imports = append(imports, 目录.模块名);
            }
            else {
                for (let s of 目录.thenStatement.statements) {
                    return 收集导入成员模块(s, imports);
                }
                if (目录.elseStatement) {
                    if (是导入版本成员(目录.elseStatement)) {
                        return 收集导入成员模块(目录.elseStatement, imports);
                    }
                    else {
                        for (let s of 目录.elseStatement.statements) {
                            return 收集导入成员模块(s, imports);
                        }
                    }
                }
            }
            return imports;
        }

        function getSourceFileFromReferenceWorker(
            fileName: string,
            getSourceFile: (fileName: string) => SourceFile | undefined,
            fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void,
            refFile?: SourceFile): SourceFile | undefined {

            if (hasExtension(fileName)) {
                if (!forEach(supportedExtensionsWithJsonIfResolveJsonModule, extension => fileExtensionIs(host.getCanonicalFileName(fileName), extension))) {
                    if (fail) fail(Diagnostics.File_0_has_unsupported_extension_The_only_supported_extensions_are_1, fileName, "'" + supportedExtensions.join("', '") + "'");
                    return undefined;
                }

                const sourceFile = getSourceFile(fileName);
                if (fail) {
                    if (!sourceFile) {
                        fail(Diagnostics.没发现文件_0, fileName);
                    }
                    else if (refFile && host.getCanonicalFileName(fileName) === host.getCanonicalFileName(refFile.fileName)) {
                        fail(Diagnostics.A_file_cannot_have_a_reference_to_itself);
                    }
                }
                return sourceFile;
            }
            else {
                if (fail) {
                    fail(Diagnostics.没发现文件_0, fileName);
                    return undefined;
                }
            }
        }

        /** This has side effects through `findSourceFile`. */
        function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: RefFile): void {
            getSourceFileFromReferenceWorker(
                fileName,
                fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile), // TODO: GH#18217
                (diagnostic, ...args) => fileProcessingDiagnostics.add(
                    createRefFileDiagnostic(refFile, diagnostic, ...args)
                ),
                refFile && refFile.file
            );
        }

        function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFileName: string, refFile: RefFile | undefined): void {
            fileProcessingDiagnostics.add(createRefFileDiagnostic(
                refFile,
                Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing,
                fileName,
                existingFileName
            ));
        }

        // Get source file from normalized fileName
        function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile: RefFile | undefined): SourceFile | undefined {
            const originalFileName = fileName;
            if (filesByName.has(path)) {
                const file = filesByName.get(path);
                // try to check if we've already seen this file but with a different casing in path
                // NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected
                if (file) {
                    let inputName = fileName;
                    const checkedName = file.fileName;
                    // Check if it differs only in drive letters its ok to ignore that error:
                    const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory);
                    const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(inputName, currentDirectory);
                    if (checkedAbsolutePath !== inputAbsolutePath) {
                        reportFileNamesDifferOnlyInCasingError(inputName, checkedName, refFile);
                    }
                }

                // If the file was previously found via a node_modules search, but is now being processed as a root file,
                // then everything it sucks in may also be marked incorrectly, and needs to be checked again.
                if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth === 0) {
                    sourceFilesFoundSearchingNodeModules.set(file.path, false);
                    modulesWithElidedImports.set(file.path, false);
                    processImportedModules(file);
                }
                // See if we need to reprocess the imports due to prior skipped imports
                else if (file && modulesWithElidedImports.get(file.path)) {
                    if (currentNodeModulesDepth < maxNodeModuleJsDepth) {
                        modulesWithElidedImports.set(file.path, false);
                        processImportedModules(file);
                    }
                }

                addFileToRefFileMap(file || undefined, refFile);
                return file || undefined;
            }

            let redirectedPath: Path | undefined;

            // We haven't looked for this file, do so now and cache result
            const file = host.getSourceFile(
                fileName,
                hostErrorMessage => fileProcessingDiagnostics.add(createRefFileDiagnostic(
                    refFile,
                    Diagnostics.Cannot_read_file_0_Colon_1,
                    fileName,
                    hostErrorMessage
                )),
                shouldCreateNewSourceFile
            );

            addFileToFilesByName(file, path, redirectedPath);

            if (file) {
                sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
                file.path = path;
                file.resolvedPath = toPath(fileName);
                file.originalFileName = originalFileName;

                if (host.useCaseSensitiveFileNames()) {
                    const pathLowerCase = path.toLowerCase();
                    // for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
                    const existingFile = filesByNameIgnoreCase!.get(pathLowerCase);
                    if (existingFile) {
                        reportFileNamesDifferOnlyInCasingError(fileName, existingFile.fileName, refFile);
                    }
                    else {
                        filesByNameIgnoreCase!.set(pathLowerCase, file);
                    }
                }

                // always process imported modules to record module name resolutions
                processImportedModules(file);

                if (isDefaultLib) {
                    processingDefaultLibFiles!.push(file);
                }
                else {
                    processingOtherFiles!.push(file);
                }
            }
            addFileToRefFileMap(file, refFile);
            return file;
        }

        function addFileToRefFileMap(file: SourceFile | undefined, refFile: RefFile | undefined) {
            if (refFile && file) {
                (refFileMap || (refFileMap = createMultiMap())).add(file.path, {
                    kind: refFile.kind,
                    index: refFile.index,
                    file: refFile.file.path
                });
            }
        }

        function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) {
            if (redirectedPath) {
                filesByName.set(redirectedPath, file);
                filesByName.set(path, file || false);
            }
            else {
                filesByName.set(path, file);
            }
        }

        function createRefFileDiagnostic(refFile: RefFile | undefined, message: DiagnosticMessage, ...args: any[]): Diagnostic {
            if (!refFile) {
                return createCompilerDiagnostic(message, ...args);
            }
            else {
                return createFileDiagnostic(refFile.file, refFile.pos, refFile.end - refFile.pos, message, ...args);
            }
        }

        function getCanonicalFileName(fileName: string): string {
            return host.getCanonicalFileName(fileName);
        }

        function processImportedModules(file: SourceFile) {
            collectExternalModuleReferences(file);
            if (file?.imports?.length) {
                // Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
                const moduleNames = getModuleNames(file);
                const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.originalFileName, currentDirectory), file);
                Debug.assert(resolutions.length === moduleNames.length);
                for (let i = 0; i < moduleNames.length; i++) {
                    const resolution = resolutions[i];
                    setResolvedModule(file, moduleNames[i], resolution);
                    if (!resolution) {
                        programDiagnostics.add(createFileDiagnostic(file, file?.imports[i].pos, file?.imports[i].end - file?.imports[i].pos, Diagnostics.没找到模块_0, moduleNames[i]));
                        continue;
                    }
                    const isFromNodeModulesSearch = resolution.是外部支持库导入;
                    const resolvedFileNames = host.readDirectory!(resolution.解析的模块目录路径, [Extension.Dk, Extension.K], [Extension.Json, Extension.TestK, Extension.KBuildInfo], [Extension.K, Extension.Dk]);
                    if (isFromNodeModulesSearch) {
                        currentNodeModulesDepth++;
                    }

                    // Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
                    // This may still end up being an untyped module -- the file won't be included but imports will be allowed.
                    const shouldAddFile = resolvedFileNames
                        && i < file.imports.length
                        && (!(file.imports[i].flags & NodeFlags.JSDoc));

                    if (shouldAddFile) {
                        for (let ii = 0; ii < resolvedFileNames.length; ii++) {
                            const resolvedFileName = resolvedFileNames[ii]
                            const path = toPath(resolvedFileName);
                            const pos = skipTrivia(file.text, file.imports[i].pos);
                            const 模块文件 = findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, {
                                kind: RefFileKind.Import,
                                index: i,
                                file,
                                pos,
                                end: file.imports[i].end
                            });
                            if (模块文件) {
                                if (!resolution.全部源码文件名) resolution.全部源码文件名 = [];
                                resolution.全部源码文件名.push(模块文件.fileName);
                                if (!resolution.模块名称) resolution.模块名称 = 查找程序声明名称(模块文件);
                            }
                        }
                    }

                    if (isFromNodeModulesSearch) {
                        currentNodeModulesDepth--;
                    }
                }
            }
            else {
                // no imports - drop cached module resolutions                
                file.resolvedModules = undefined;
            }
        }

        function 查找程序声明名称(f: SourceFile){
            if(f.statements){
                for(const s of f.statements){
                    if(s.kind === SyntaxKind.程序声明_){
                        return (s as 程序声明_).name;
                    }
                }
            }
        }

        function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string {
            const fileNames = mapDefined(sourceFiles, file => file.isDeclarationFile ? undefined : file.fileName);
            return computeCommonSourceDirectoryOfFilenames(fileNames, currentDirectory, getCanonicalFileName);
        }

        function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean {
            let allFilesBelongToPath = true;
            const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
            let rootPaths: Map<true> | undefined;

            for (const sourceFile of sourceFiles) {
                if (!sourceFile.isDeclarationFile) {
                    const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
                    if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
                        if (!rootPaths) rootPaths = arrayToSet(rootNames, toPath);
                        addProgramDiagnosticAtRefPath(
                            sourceFile,
                            rootPaths,
                            Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
                            sourceFile.fileName,
                            rootDirectory
                        );
                        allFilesBelongToPath = false;
                    }
                }
            }

            return allFilesBelongToPath;
        }

        function addProgramDiagnosticAtRefPath(file: SourceFile, rootPaths: Map<true>, message: DiagnosticMessage, ...args: (string | number | undefined)[]) {
            const refPaths = refFileMap && refFileMap.get(file.path);
            const refPathToReportErrorOn = forEach(refPaths, refPath => rootPaths.has(refPath.file) ? refPath : undefined) ||
                elementAt(refPaths, 0);
            if (refPathToReportErrorOn) {
                const refFile = Debug.assertDefined(getSourceFileByPath(refPathToReportErrorOn.file));
                const { kind, index } = refPathToReportErrorOn;
                let pos: number, end: number;
                switch (kind) {
                    case RefFileKind.Import:
                        pos = skipTrivia(refFile.text, refFile.imports[index].pos);
                        end = refFile.imports[index].end;
                        break;
                    default:
                        return Debug.assertNever(kind);
                }
                programDiagnostics.add(createFileDiagnostic(refFile, pos, end - pos, message, ...args));
            }
            else {
                programDiagnostics.add(createCompilerDiagnostic(message, ...args));
            }
        }

        function isEmittedFile(file: string): boolean {
            // If this is source file, its not emitted file
            const filePath = toPath(file);
            if (getSourceFileByPath(filePath)) {
                return false;
            }
            if (options.输出) {
                return containsPath(options.输出, filePath, currentDirectory, !host.useCaseSensitiveFileNames());
            }
            return false;
        }


        function getProbableSymlinks(): ReadonlyMap<string> {
            if (host.getSymlinks) {
                return host.getSymlinks();
            }
            return symlinks || (symlinks = discoverProbableSymlinks(
                files,
                getCanonicalFileName,
                host.getCurrentDirectory()));
        }
    }

    /*@internal*/
    interface CompilerHostLike {
        useCaseSensitiveFileNames(): boolean;
        getCurrentDirectory(): string;
        fileExists(fileName: string): boolean;
        readFile(fileName: string): string | undefined;
        readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[];
        trace?(s: string): void;
        onUnRecoverableConfigFileDiagnostic?: DiagnosticReporter;
    }

    /* @internal */
    export function parseConfigHostFromCompilerHostLike(host: CompilerHostLike, directoryStructureHost: DirectoryStructureHost = host): ParseConfigFileHost {
        return {
            fileExists: f => directoryStructureHost.fileExists(f),
            readDirectory(root, extensions, excludes, includes, depth) {
                Debug.assertDefined(directoryStructureHost.readDirectory, "'CompilerHost.readDirectory' must be implemented to correctly process 'projectReferences'");
                return directoryStructureHost.readDirectory!(root, extensions, excludes, includes, depth);
            },
            readFile: f => directoryStructureHost.readFile(f),
            useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(),
            getCurrentDirectory: () => host.getCurrentDirectory(),
            onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic || returnUndefined,
            trace: host.trace ? (s) => host.trace!(s) : undefined
        };
    }

    // For backward compatibility
    /** @deprecated */ export interface ResolveProjectReferencePathHost {
        fileExists(fileName: string): boolean;
    }

    function getModuleNames({ imports }: SourceFile): string[] {
        const res = imports.map(i => i.text);
        return res.map(正规化模块名称);
    }
}
